package cn.turboinfo.dongying.api.provider.common.service.impl.geo

import cn.turboinfo.dongying.api.provider.common.service.geo.GeoDistance
import cn.turboinfo.dongying.api.provider.common.service.geo.GeoPosition
import cn.turboinfo.dongying.api.provider.common.service.geo.GeoService
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.geo.Distance
import org.springframework.data.geo.Metrics
import org.springframework.data.geo.Point
import org.springframework.data.redis.connection.RedisGeoCommands
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.data.redis.domain.geo.GeoReference
import org.springframework.data.redis.domain.geo.GeoShape
import org.springframework.stereotype.Service

@Service
class GeoServiceImpl(
    stringRedisTemplate: StringRedisTemplate,
) : GeoService {

    @Value("\${deploy.redis.geo.key-prefix:__geo__}")
    private lateinit var geoKeyPrefix: String

    private val geoOperations = stringRedisTemplate.opsForGeo()

    private fun storeKey(store: String) = "$geoKeyPrefix:$store"

    override fun add(store: String, member: String, longitude: Double, latitude: Double): Long {
        return geoOperations.add(storeKey(store), Point(longitude, latitude), member)!!
    }

    override fun remove(store: String, member: String): Long {
        return geoOperations.remove(storeKey(store), member)!!
    }

    override fun get(store: String, vararg members: String): List<GeoPosition?> {
        return geoOperations.position(storeKey(store), *members)!!
            .map {
                GeoPosition(it.x, it.y)
            }
    }

    override fun searchRadius(
        store: String,
        longitude: Double,
        latitude: Double,
        radiusInKilometers: Double,
        limit: Long?,
        desc: Boolean
    ): List<GeoDistance> {
        return geoOperations
            .search(
                storeKey(store),
                GeoReference.fromCoordinate(longitude, latitude),
                GeoShape.byRadius(
                    Distance(
                        radiusInKilometers,
                        Metrics.KILOMETERS
                    )
                ),
                RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs()
                    .apply {
                        if (desc) {
                            sortDescending()
                        } else {
                            sortAscending()
                        }
                    }
                    .apply {
                        if (limit != null) {
                            limit(limit)
                        }
                    }
                    .apply {
                        includeDistance()
                    }
            )!!
            .map {
                GeoDistance(it.content.name, it.distance.value)
            }
    }

}
